/*
 * $Id: RulersLayer.java,v 1.1 2003/05/27 06:24:10 znamenacek Exp $
 *
 * Copyright (c) Milan Jedlicka
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 */

package net.sf.vofce.canvas;

import java.awt.Graphics2D;
import java.awt.Color;

/**
 * Layer which contains rulers (if needed) with labels.
 * 
 * @author Milan Jedlicka
 */

public class RulersLayer implements Layer {

	/** if distance is less then this constant we use short numbers. */
	public static final int MINIMAL_MARK_DISTANCE = 20;
	
	/** Maximal cifer count of number on horizontal ruler. */
	public static final int MAX_HORIZONTAL_LABEL_WIDTH = 6;

	/** Maximal cifer count of number on vertical ruler. */
	public static final int MAX_VERTICAL_LABEL_WIDTH = 4;

	public static final double PRECISION = Math.pow(10, -4);

	/** Width of rulers in pixels. */
	public static final int RULER_WIDTH = 47;

	/** Length of marker on ruler in pixels */
	protected static final int MARK_LENGTH = 5;

	/** Constans for 4 types of rulers. */
	public static final int UP = 0;
	public static final int DOWN = 1;
	public static final int LEFT = 2;
	public static final int RIGHT = 3;
	
        /** Axis x. */
        protected Axis x;

        /** Axis y. */
        protected Axis y;

	public void setAxis(Axis x, Axis y) {
		this.x = x;
                this.y = y;
	}

	public void render(Graphics2D buffer) {
		boolean drawX = false;
		boolean drawY = false;
		
		double[] yValues = y.getValues();		
		
		buffer.setColor(Color.black);

		if (y.getZeroPosition() == -1) {
			drawX = true;
			if (yValues[0] > 0) {
				buffer.setColor(new Color(230, 163, 4));
				buffer.fillRect(0, y.getPhysicalLength()-1 - RULER_WIDTH, 
						x.getPhysicalLength()-1, 
						y.getPhysicalLength()-1 - RULER_WIDTH);
				buffer.setColor(new Color(0, 0, 0));
				buffer.drawLine(0, y.getPhysicalLength()-1 - RULER_WIDTH, 
						x.getPhysicalLength()-1, y.getPhysicalLength()-1 - RULER_WIDTH);
						drawHorizontal(buffer, DOWN);
				
			} else {
				buffer.setColor(new Color(230, 163, 4));
				buffer.fillRect(0, 0, x.getPhysicalLength()-1, RULER_WIDTH);
				buffer.setColor(new Color(0, 0, 0));
				buffer.drawLine(0, RULER_WIDTH, x.getPhysicalLength()-1, RULER_WIDTH);
				drawHorizontal(buffer, UP);
			}
		}

		double[] xValues = x.getValues();		
		
		if (x.getZeroPosition() == -1) {
			drawY = true;
			if (xValues[x.getPhysicalLength()-1] < 0) {
				buffer.setColor(new Color(230, 163, 4));
				buffer.fillRect(x.getPhysicalLength()-1 - RULER_WIDTH, 0, 
						RULER_WIDTH, 
						y.getPhysicalLength()-1);
				buffer.setColor(new Color(0, 0, 0));
				buffer.drawLine(x.getPhysicalLength()-1 - RULER_WIDTH, 0, 
						x.getPhysicalLength()-1 - RULER_WIDTH, 
						y.getPhysicalLength()-1);
				drawVertical(buffer, RIGHT);
				
			} else {
				buffer.setColor(new Color(230, 163, 4));
                                buffer.fillRect(0, 0, RULER_WIDTH, 
						y.getPhysicalLength()-1);
				buffer.setColor(new Color(0, 0, 0));
				buffer.drawLine(RULER_WIDTH, 0, 
						RULER_WIDTH, y.getPhysicalLength()-1);
				drawVertical(buffer, LEFT);
			}
		}

		if (drawX && drawY) {
			buffer.setColor(new Color(230, 163, 4));
			if (yValues[0] > 0) {
				if (xValues[x.getPhysicalLength()-1] < 0) {
					buffer.drawRect(x.getPhysicalLength()-1 - RULER_WIDTH, 
							y.getPhysicalLength()-1 - RULER_WIDTH, 
							RULER_WIDTH, 
							RULER_WIDTH);
				} else {
					buffer.fillRect(0, y.getPhysicalLength()-1 - RULER_WIDTH, 
							RULER_WIDTH, 
							RULER_WIDTH); 
				}
			} else {
				if (xValues[x.getPhysicalLength()-1] < 0) {
					buffer.fillRect(x.getPhysicalLength()-1 - RULER_WIDTH, 0, 
							RULER_WIDTH, 
							RULER_WIDTH);
				} else {
					buffer.fillRect(0, 0, RULER_WIDTH, RULER_WIDTH);
				}
			}
		}

		if (drawX) {
			buffer.setColor(new Color(0, 0, 0));
			if (yValues[0] > 0) {
				buffer.drawLine(0, y.getPhysicalLength()-1 - RULER_WIDTH, 
						x.getPhysicalLength()-1, y.getPhysicalLength()-1 - RULER_WIDTH);
			} else {
				buffer.drawLine(0, RULER_WIDTH, 
						x.getPhysicalLength()-1, RULER_WIDTH);
			}
		}
	
		if (drawY) {
			buffer.setColor(new Color(0, 0, 0));
			if (xValues[x.getPhysicalLength()-1] < 0) {
				buffer.drawLine(x.getPhysicalLength()-1 - RULER_WIDTH, 0, 
						x.getPhysicalLength()-1 - RULER_WIDTH, y.getPhysicalLength()-1);
			} else {
				buffer.drawLine(RULER_WIDTH, 0, 
						RULER_WIDTH, y.getPhysicalLength()-1);
			}
		}
	}

	/** Draws marked values on horizontal ruler. */
	public void drawHorizontal(Graphics2D buffer, int where) {
		int rulerPosition;
		int numberPosition;
		
		switch(where) {
			case UP:
				rulerPosition =  RULER_WIDTH;
				numberPosition = rulerPosition - 10;
				break;
			case DOWN: 
				rulerPosition = y.getPhysicalLength()-1 - RULER_WIDTH;
				numberPosition = rulerPosition + 20;
				break;
			default:
				rulerPosition = 0;
				numberPosition = 0;
		}
		
		int[] marks;
		double[] values;

		marks = x.getMarks();
		values = x.getValues();


		switch(where) {
			case UP: 
				for (int i = 0; i < marks.length; i++) {
					buffer.drawLine(marks[i], rulerPosition, 
							marks[i], rulerPosition-MARK_LENGTH);
				}
				break;
		
			case DOWN:
				for (int i = 0; i < marks.length; i++) {
					buffer.drawLine(marks[i], rulerPosition, 
							marks[i], rulerPosition+MARK_LENGTH);
				}
				break;
		}

		int axisPosition = y.getZeroPosition();
		

		for (int i = 0; i < marks.length; i++) {
			String shortNumber = getShortNumber(values[marks[i]], MAX_HORIZONTAL_LABEL_WIDTH);
		
			boolean isShortNumber = false;
			if (shortNumber != null) {
				isShortNumber = true;	
			}
		
			if (values[marks[i]] != 0f) {
				if (equals(Math.round(values[marks[i]]), values[marks[i]])) {
					if (isShortNumber) {
						buffer.drawString(shortNumber,  
							marks[i]-getXPosition((double) Math.pow(10, shortNumber.length()-1)), 
							numberPosition);
					} else {
						buffer.drawString(Integer.toString((int) values[marks[i]]), 
							marks[i]-getXPosition(values[marks[i]]), 
							numberPosition);
					}
				} else {
					if (isShortNumber) {
						buffer.drawString(shortNumber,  
							marks[i]-getXPosition((double) Math.pow(10, shortNumber.length()-1)),
							numberPosition);	
					} else {
						buffer.drawString(Double.toString(values[marks[i]]), 
							marks[i]-getXPosition(values[marks[i]]),
							numberPosition); 
					}
				}
			} 
		}
		
	}

	/** Draws marked values on vertical ruler. */
	public void drawVertical(Graphics2D buffer, int where) {
		int rulerPosition;
		int numberPosition;
		
		switch(where) {
			case LEFT:
				rulerPosition =  RULER_WIDTH;
				numberPosition = rulerPosition - 30;
				break;
			case RIGHT: 
				rulerPosition = x.getPhysicalLength()-1 - RULER_WIDTH;
				numberPosition = rulerPosition + 10;
				break;
			default:
				rulerPosition = 0;
				numberPosition = 0;
		}

		int[] marks;
		double[] values;
		
		marks = y.getMarks();
		values = y.getValues();

		switch(where) {
			case LEFT: 
				for (int i = 0; i < marks.length; i++) {
					buffer.drawLine(rulerPosition, marks[i], 
							rulerPosition-MARK_LENGTH, marks[i]);
				}
				break;
		
			case RIGHT:
				for (int i = 0; i < marks.length; i++) {
					buffer.drawLine(rulerPosition, marks[i], 
							rulerPosition+MARK_LENGTH, marks[i]);
				}
				break;
		}
		
		int axisPosition = x.getZeroPosition();
	

		switch(where) {
			case RIGHT:			
			for (int i = 0; i < marks.length; i++) { // start FOR
				String shortNumber = getShortNumber(values[marks[i]], MAX_VERTICAL_LABEL_WIDTH);
				
			boolean isShortNumber = false;
			if (shortNumber != null) {
				isShortNumber = true;	
			}
				
			if (values[marks[i]] != 0f) {
				if (equals(Math.round(values[marks[i]]), values[marks[i]])) {
					if (isShortNumber) {
						buffer.drawString(shortNumber,
								numberPosition, 
								marks[i]+5);	
					} else {
						buffer.drawString(Integer.toString((int) values[marks[i]]), 
								numberPosition, 
								marks[i]+5);
					}
				} else {
					if (isShortNumber) {
						buffer.drawString(shortNumber, 
								numberPosition, 
								marks[i]+5);
					} else {
						buffer.drawString(Double.toString(values[marks[i]]), 
								numberPosition, 
								marks[i]+5);	
					}
				}
			} 
			} // end FOR
			break;
			case LEFT:
			for (int i = 0; i < marks.length; i++) { // start FOR
				String shortNumber = getShortNumber(values[marks[i]], MAX_VERTICAL_LABEL_WIDTH);
			
				boolean isShortNumber = false;
				if (shortNumber != null) {
					isShortNumber = true;	
				}
			
			if (values[marks[i]] != 0f) {
				if (equals(Math.round(values[marks[i]]), values[marks[i]])) {
					if (isShortNumber) {
						buffer.drawString(shortNumber, 
								numberPosition-getYPointPosition((double) Math.pow(10, shortNumber.length()-1)), 
								marks[i]+5);
					} else {
						buffer.drawString(Integer.toString((int) values[marks[i]]), 
								numberPosition-getYPointPosition(values[marks[i]]), 
								marks[i]+5);							
					}
				} else {
					if (isShortNumber) {
						buffer.drawString(shortNumber, 
								numberPosition-getYPointPosition((double) Math.pow(10, shortNumber.length()-1)), 
								marks[i]+5);
					} else {
						buffer.drawString(Double.toString(values[marks[i]]), 
								numberPosition-getYPointPosition(values[marks[i]]),
								marks[i]+5);
					}
				}
			}
			} // end FOR
			break;
		}
	}

	/** Converts number into short number representation. */
	public static String getShortNumber(double number, int maxLength) {
		int counter = 0;
		if (!((number != 0f) && (Double.toString(number).length() > maxLength))) {
			return null;	
		}

		if (number > 0) {
			if (Math.abs(number) > 100) {
				while (Math.abs(number) > 100) {
					counter++;
					number /= 10;
				}
			} else {
				while (Math.abs(number) < 10) {
					counter--;
					number *= 10;
				}
			}
		} else {
			if (Math.abs(number) > 100) {
				while (Math.abs(number) > 10) {
					counter++;
					number /= 10;
				}
			} else {
				while (Math.abs(number) < 1) {
					counter--;
					number *= 10;
				}
			}
		}
	
		return Integer.toString((int) number) + "E" + counter;
	
	}

	/** Counts position of value on horizontal axis. */
	protected int getXPosition(double number) {
		String numberToString = Double.toString(number);
		int ciferCount = numberToString.length();

		return Math.round((6*ciferCount/2)-6);

	}

	/** Counts position of value on vertical axis. */
	protected int getYPointPosition(double number) {
		String numberToString;
		
		if (Math.round(number) == number) {
			numberToString = Integer.toString((int) number);
		} else {
			numberToString = Double.toString(number);
		}

		int ciferCount = numberToString.length();
		return Math.round((6*ciferCount/2)-((4-ciferCount)*4)-ciferCount);
	}	
	
	/** Comparing numbers with given precision. */
	protected boolean equals(double a, double b) {
		return Math.abs(a - b) < PRECISION;
	}

}
